package club.kynb.mall.application.service;

import club.kynb.mall.application.constant.LockKeyConst;
import club.kynb.mall.application.context.MallUserContext;
import club.kynb.mall.application.context.OrderContext;
import club.kynb.mall.application.context.OrderPreviewContext;
import club.kynb.mall.application.context.OrderSubmitContext;
import club.kynb.mall.application.convert.MallCommonConvertor;
import club.kynb.mall.application.model.model.command.*;
import club.kynb.mall.application.model.model.dto.UserOrderDTO;
import club.kynb.mall.application.model.model.vo.OrderPreviewResultVO;
import club.kynb.mall.application.model.model.vo.OrderSubmitResultVO;
import club.kynb.mall.order.api.IOrderRefundService;
import club.kynb.mall.order.api.IOrderService;
import club.kynb.mall.order.api.IShoppingCartService;
import club.kynb.mall.order.constant.*;
import club.kynb.mall.order.model.dto.*;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.pizza.common.web.exception.Errors;
import org.pizza.common.web.response.CommonCode;
import org.pizza.constant.GlobalConstant;
import org.pizza.model.page.PageResult;
import org.pizza.statemachine.StateMachine;
import org.pizza.util.Checker;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;


/**
 * @author kynb_club@163.com
 * @since 2021/7/4 3:02 下午
 */
@Slf4j
@Service
@AllArgsConstructor
public class OrderApplicationService {
    private final RedissonClient redissonClient;
    private final IOrderService iOrderService;
    private final IOrderRefundService iOrderRefundService;
    private final IShoppingCartService iShoppingCartService;
    private final StateMachine<OrderStatusEnum, OrderEventEnum, OrderContext> orderStateMachine;

    public OrderPreviewResultVO previewOrder() {
        final OrderPreviewContext orderPreviewContext = new OrderPreviewContext();
        orderStateMachine.fireEvent(OrderStatusEnum.NONE, OrderEventEnum.PREVIEW, orderPreviewContext);
        return orderPreviewContext.getOrderPreviewResultVO();
    }


    public OrderSubmitResultVO submitOrder(SubmitOrderCommand command) {
        boolean isLocked = false;
        String key = String.format("%s:%s", LockKeyConst.SUBMIT_ORDER, command.getInnerOrderNo());
        final RLock lock = redissonClient.getLock(key);
        try {
            Checker.ifNotThrow(isLocked = lock.tryLock(), () -> Errors.BIZ.exception(CommonCode.BUSY));
            boolean existsOrderNo = iOrderService.existsOrderNo(command.getInnerOrderNo());
            Checker.ifThrow(existsOrderNo,()->Errors.BIZ.exception("订单已提交！"));
            final OrderSubmitContext orderSubmitContext = new OrderSubmitContext();
            orderSubmitContext.setCommand(command);
            orderStateMachine.fireEvent(OrderStatusEnum.INIT, OrderEventEnum.CREATE, orderSubmitContext);
            return orderSubmitContext.getOrderSubmitResultVO();
        } finally {
            if (isLocked) {
                lock.unlock();
            }
        }

    }


    public PageResult<UserOrderDTO> orderHistoryPageQuery(OrderHistoryPageQuery orderHistoryPageQuery) {
        final Long userId = MallUserContext.get(true).getUserDTO().getId();
        orderHistoryPageQuery.setUserId(userId);
        final PageResult<OrderAndDetailsDTO> pageResult = iOrderService.orderHistoryPageQuery(orderHistoryPageQuery);
        return MallCommonConvertor.covert(pageResult);
    }

    public PageResult<OrderRefundDTO> orderRefundHistory(OrderRefundHistoryPageQuery pageQuery) {
        final Long userId = MallUserContext.get(true).getUserDTO().getId();
        pageQuery.setUserId(userId);
        return iOrderRefundService.orderRefundHistoryPageQuery(pageQuery);
    }


    public UserOrderDTO orderDetail(String innerOrderNo) {
        final Optional<OrderAndDetailsDTO> orderAndDetailsOptional = iOrderService.getByOrderAndDetails(innerOrderNo);
        final OrderAndDetailsDTO orderAndDetailsDTO = orderAndDetailsOptional.orElseThrow(() -> Errors.BIZ.exception("订单号有误，找不到订单信息"));
        return MallCommonConvertor.covertUserOrderDTO(orderAndDetailsDTO);
    }

    public void orderCancel(CancelOrderCommand command) {
        command.setCancelTypeEnum(OrderCancelTypeEnum.MANUAL_CANCELLED);
        orderStateMachine.fireEvent(OrderStatusEnum.UNPAID, OrderEventEnum.MANUAL_CANCELLED, () -> command);
    }

    public void orderConfirm(ConfirmOrderCommand command) {
        command.setConfirmTypeEnum(OrderConfirmTypeEnum.MANUAL_CONFIRM);
        orderStateMachine.fireEvent(OrderStatusEnum.UN_RECEIVE, OrderEventEnum.MANUAL_CONFIRM, () -> command);
    }

    public void orderAgain(AgainOrderCommand command) {
        final Long userId = MallUserContext.get(true).getUserDTO().getId();
        final Optional<OrderAndDetailsDTO> orderAndDetailsOptional = iOrderService.getByOrderAndDetails(command.getInnerOrderNo());
        final OrderAndDetailsDTO orderAndDetailsDTO = orderAndDetailsOptional.orElseThrow(() -> Errors.BIZ.exception("订单号有误，找不到订单信息"));
        final List<ShoppingCartDTO> shoppingCartList = orderAndDetailsDTO.getDetails().stream().map(orderDetailDTO -> {
            final ShoppingCartDTO shoppingCartDTO = new ShoppingCartDTO();
            shoppingCartDTO.setUserId(userId);
            shoppingCartDTO.setSpuId(String.valueOf(orderDetailDTO.getSpuId()));
            shoppingCartDTO.setSkuId(String.valueOf(orderDetailDTO.getSkuId()));
            shoppingCartDTO.setAddChannel(ShoppingCartConstant.ShoppingCartChannelEnum.APP_MALL.name());
            shoppingCartDTO.setSkuNum(orderDetailDTO.getSkuNum());
            shoppingCartDTO.setChecked(GlobalConstant.YES);
            return shoppingCartDTO;
        }).collect(Collectors.toList());
        boolean isLocked = false;
        String key = String.format("%s:%s", LockKeyConst.ADD_SHOPPING_CART, userId);
        final RLock lock = redissonClient.getLock(key);
        try {
            Checker.ifNotThrow(isLocked = lock.tryLock(), () -> Errors.BIZ.exception(CommonCode.BUSY));
            iShoppingCartService.batchAdd(shoppingCartList);
        } finally {
            if (isLocked) {
                lock.unlock();
            }
        }
    }

    public void orderRefund(RefundOrderCommand command) {
        OrderDTO orderDTO = iOrderService.getOrderBy(command.getInnerOrderNo()).orElseThrow(() -> Errors.BIZ.exception("订单编号有误"));
        OrderEventEnum orderEventEnum = null;
        OrderStatusEnum fromStatus = null;
        if(OrderStatusEnum.UN_RECEIVE.code().equals(orderDTO.getOrderStatus())){
            fromStatus = OrderStatusEnum.UN_RECEIVE;
            orderEventEnum = OrderEventEnum.UN_RECEIVE_APPLY_REFUND;
        }else if(OrderStatusEnum.UN_DELIVERY.code().equals(orderDTO.getOrderStatus())){
            fromStatus = OrderStatusEnum.UN_DELIVERY;
            orderEventEnum = OrderEventEnum.UN_DELIVERY_APPLY_REFUND;
        }
        Checker.ifNullThrow(fromStatus,()->Errors.PARAM.exception("订单状态已更改，请刷新"));
        orderStateMachine.fireEvent(fromStatus, orderEventEnum, () -> command);
    }

    public void orderRefundCancel(RefundOrderCancelCommand command) {
        OrderEventEnum orderEventEnum = null;
        if(OrderStatusEnum.UN_RECEIVE.code().equals(command.getOrderOriginalStatus())){
            orderEventEnum = OrderEventEnum.UN_RECEIVE_REFUND_CANCEL;
        }else if (OrderStatusEnum.UN_DELIVERY.code().equals(command.getOrderOriginalStatus())){
            orderEventEnum = OrderEventEnum.UN_DELIVERY_REFUND_CANCEL;
        }
        Checker.ifNullThrow(orderEventEnum,()->Errors.PARAM.exception("退款订单原始状态非法"));
        orderStateMachine.fireEvent(OrderStatusEnum.WAITING_REFUND, orderEventEnum, () -> command);

    }

}
